﻿var DEV_MODE = true;

var map = {};
var tagManagerView = {};
var tagManagerAdd = {};

var gActiveTrackManager = {};

var gTags = {};
var gTrackManager = {};
var gFilteredTrackManager = {};
var gMapTrackView = {};
var gVisibleTracksWidget = {};

//todo: track changes in tags of individual tracks
FilteredTrackManager = function( trackManager, tagManager )
{
    var m_trackManager = trackManager;
    var m_tagManager = tagManager;
    var m_filteredTracks = {};
    
    this.onTrackChanged = function(event, trackID, trackStatus)
    {
        if ( trackID )
        {
            if ( trackStatus == TrackManager.STATUS_REMOVED && trackID in m_filteredTracks )
            {
                delete m_filteredTracks[trackID];
                $(this).trigger( 'onchange', [trackID, trackStatus] );
            }
            if ( trackStatus == TrackManager.STATUS_ADDED )
            {
                var addedTrack = m_trackManager.getTrack(trackID);
                
                if ( addedTrack.isSelectedByTags( m_tagManager ) )
                {
                    m_filteredTracks[trackID] = addedTrack;
                    $(this).trigger( 'onchange', [trackID, trackStatus] );
                }
            }
        }
        else
        { //trackID == null -> rebuild all the list
            m_filteredTracks = {};
            var ids = m_trackManager.getAllTrackIDs();
            
            for ( var k = 0; k < ids.length; k++ )
            {
                var track = m_trackManager.getTrack( ids[k] );
                if ( track.isSelectedByTags( m_tagManager ) )
                    m_filteredTracks[ ids[k] ] = track;
            }
            
            $(this).trigger( 'onchange', [trackID, trackStatus] );
        }
    }
    
    //TODO: do not rebuild whole the list...
    this.onTagChanged = function( event, tagID, tagState )
    {
        this.onTrackChanged( null, null, null );
    }

    this.getTrack = function( trackID )
    {
        return m_filteredTracks[ trackID ];
    }
    
    this.isTrackFiltered = function( trackID )
    {
        return (trackID in m_filteredTracks);
    }
    
    $(m_trackManager).bind( 'onchange', $.proxy( this, 'onTrackChanged' ) );
    $(m_tagManager).bind( 'onchange', $.proxy( this, 'onTagChanged' ) );
    
    this.onTrackChanged( null, null, null );
}

MapTrackView = function( trackManager, filteredTrackManager, activeTrackManager )
{
    var m_trackManager = trackManager;
    var m_filteredTrackManager = filteredTrackManager;
    var m_activeTrackManager = activeTrackManager;
    
    this.onTrackChanged = function( event, trackID, trackState )
    {
        var ids2update = [];
        if ( trackID )
            ids2update.push( trackID )
        else
            ids2update = m_trackManager.getAllTrackIDs();
            
        for ( var k = 0; k < ids2update.length; k++ )
        {
            var curID = ids2update[k];
            if ( !m_filteredTrackManager.isTrackFiltered( curID ) )
            {
                m_trackManager.getTrack( curID ).changeStatus( POLYLINE_STATUS_INVISIBLE );
                continue;
            }
            
            if ( m_activeTrackManager.isTrackActive( curID ) )
                m_trackManager.getTrack( curID ).changeStatus( POLYLINE_STATUS_VISIBLE_ACTIVE )
            else
                m_trackManager.getTrack( curID ).changeStatus( POLYLINE_STATUS_VISIBLE ); 
        }
    }

    $(m_filteredTrackManager).bind( 'onchange', $.proxy( this, 'onTrackChanged' ) );
    $(m_activeTrackManager).bind( 'onchange', $.proxy( this, 'onTrackChanged' ) );
}

VisibleTracksWidget = function( targetDiv, trackManager, filteredTrackManager, activeTrackManager )
{
    var m_trackManager = trackManager;
    var m_filteredTrackManager = filteredTrackManager;
    var m_activeTrackManager = activeTrackManager;
    
    var m_visibleTracks = {};
    
    var m_$targetDiv = $( targetDiv );
    
    this.onTrackChanged = function( event, trackID, trackState )
    {
        this.rebuildTrackList();
    }
    
    this.rebuildTrackList = function()
    {
        m_$targetDiv.empty();
        
        for ( var trackID in m_visibleTracks )
            if ( m_filteredTrackManager.isTrackFiltered( trackID ) )
            {
                var divClass = m_activeTrackManager.isTrackActive( trackID ) ? 'visible_active_track_div' : 'visible_track_div';
                
                var $VisibleTrackDiv = $('<div></div>')
                                            .html(m_trackManager.getTrack( trackID ).filename)
                                            .addClass(divClass)
                                            .attr({id: trackID, title: m_trackManager.getTrack( trackID ).short_description})
                                            .click(onClickVisibleTrack);
                m_$targetDiv.append($VisibleTrackDiv);
            }
    }
    
    this.updateVisibleTracks = function( curBounds )
    {
        m_visibleTracks = {};
        
        var ids = m_trackManager.getAllTrackIDs();
        
        for ( var k = 0; k < ids.length; k++ )
        {
            var track = m_trackManager.getTrack( ids[k] );
            if ( track.isVisibleInBounds( curBounds ) )
                m_visibleTracks[ ids[k] ] = track;
        }
        
        this.rebuildTrackList();
    }
    
    $(m_filteredTrackManager).bind( 'onchange', $.proxy( this, 'onTrackChanged' ) );
    $(m_activeTrackManager).bind( 'onchange', $.proxy( this, 'onTrackChanged' ) );
}

///////////////////////////////////////////////////////////////////////////////
Array.prototype.map = function(fn) {
    var r = [];
    var l = this.length;
    for(i=0;i<l;i++)
    {
        r.push(fn(this[i]));
    }
    return r; 
};

// function eliminateDuplicates(arr) {
    // var i,
        // len=arr.length,
        // out=[],
        // obj={};

    // for (i=0;i<len;i++) {
        // obj[arr[i]]=0;
    // }
    // for (i in obj) {
        // out.push(i);
    // }
    // return out;
// }

///////////////////////////////////////////////////////////////////////////////
function assert( checkCondition, errorMessage )
{
    if ( checkCondition ) return;
    
    if ( errorMessage ) 
        alert( "Internal assertion: " + errorMessage );
    else
        alert( "Internal assertion!");
}

function onClickVisibleTrack()
{
    //alert(this.id);
    gActiveTrackManager.activateTracks([this.id]);
}

function onUpdateVisibleTracks()
{
    gVisibleTracksWidget.updateVisibleTracks( map.getBounds() );
}

///////////////////////////////////////////////////////////////////////////////
function initTags()
{
    document.getElementById("view_tags").innerHTML = "Loading tags...";
    document.getElementById("add_tags" ).innerHTML = "Loading tags...";
    
    tagManagerView = new TagManager( document.getElementById("view_tags") );
    tagManagerAdd  = new TagManager( document.getElementById("add_tags" ) );
        
    
    GDownloadUrl("cgi-bin/tags.cgi", function(data, status)
    {
        if (status != 200)
        {
            alert("Error: " + data);
            return;
        }
        
        var tmpTags = eval('(' + data + ')');
        tagManagerView.setTagList( tmpTags, ['inactive_tag', 'required_tag', 'excluded_tag'], 0 );
        tagManagerAdd.setTagList( tmpTags, ['inactive_tag', 'active_tag'], 0 );
        
        gTags = {};
        for (var k in tmpTags)
            gTags[tmpTags[k].id] = tmpTags[k].tag;
    });
}

///////////////////////////////////////////////////////////////////////////////
function initialize()
{
    var serverTicket = readCookie("ticket");
    
    if ( DEV_MODE )
    {
        if ( serverTicket )
        {
            var pt = serverTicket.split('*');
            document.getElementById("show_login").innerHTML = "Login: " + readCookie("login") + "; Ticket: " + serverTicket;
            document.getElementById("login").style.display = "none";
        }
        else
        {
            document.getElementById("loggined").style.display = "none";
            document.getElementById("upload_div").style.display = "none";
            document.getElementById("add_tag").style.display = "none";
        }
    }
    else
    {
        document.getElementById("loggined").style.display = "none";
        document.getElementById("upload_div").style.display = "none";
        document.getElementById("add_tag").style.display = "none";
        document.getElementById("login").style.display = "none";
    }
    
    var mapCanvas = document.getElementById("map_canvas");
    var canvasHeight = screen.height/2;
    mapCanvas.style.height = canvasHeight + "px";
    if (GBrowserIsCompatible())
    {
        // Make sure that SVG is on.
        if (document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#SVG", "1.1"))
        {
            _mSvgEnabled = true;
            _mSvgForced = true;
        }
        map = new GMap2(mapCanvas);
        map.setCenter(new GLatLng(55.78, 37.98), 7);
        map.setUIToDefault();
        map.enableScrollWheelZoom();
        
        var arbaletMap = GetArbaletMap();
        map.addMapType(arbaletMap);
        map.removeMapType(G_NORMAL_MAP);
        map.removeMapType(G_SATELLITE_MAP)
        map.removeMapType(G_HYBRID_MAP);
        map.addMapType(G_SATELLITE_MAP)
        map.addMapType(G_HYBRID_MAP);
        map.addMapType(G_NORMAL_MAP);
        map.addMapType(G_PHYSICAL_MAP);
        arbaletMap.getName = function (a) { return a ? 'Arb' : 'Arbalet'; };
        arbaletMap.getAlt = function () { return 'Show Arbalet topography map'; };
        G_NORMAL_MAP.getName = function (a) { return 'Map'; };
        G_NORMAL_MAP.getAlt = function () { return 'Show street map'; };
        G_SATELLITE_MAP.getName = function (a) { return a ? 'Sat' : 'Satellite'; };
        G_SATELLITE_MAP.getAlt = function () { return 'Show satellite imagery'; };
        G_HYBRID_MAP.getName = function (a) { return a ? 'Hyb' : 'Hybrid'; };
        G_HYBRID_MAP.getAlt = function () { return 'Show imagery with street names'; };
        G_PHYSICAL_MAP.getName = function (a) { return a ? 'Ter' : 'Terrain'; };
        G_PHYSICAL_MAP.getAlt = function () { return 'Show street map with terrain'; };
        map.setMapType( arbaletMap );

        GEvent.addListener( map, "click", onMapClick );
    }
    
    initTags();
    
    gTrackManager         = new TrackManager();
    gFilteredTrackManager = new FilteredTrackManager( gTrackManager, tagManagerView );
    gActiveTrackManager   = new ActiveTrackManager( document.getElementById("click_events"), gFilteredTrackManager );
    gMapTrackView         = new MapTrackView( gTrackManager, gFilteredTrackManager, gActiveTrackManager );
    gVisibleTracksWidget  = new VisibleTracksWidget( document.getElementById("visible_tracks"), gTrackManager, gFilteredTrackManager, gActiveTrackManager );
}

///////////////////////////////////////////////////////////////////////////////
function addTag()
{
    var tagname = document.getElementById("new_tagname").value;
    GDownloadUrl("cgi-bin/tags.cgi?action=add&tagname="+tagname, function(data, status)
    {
        if (status != 200)
        {
            alert("Error: " + data);
            return;
        }
        initTags();
    });
}

function onMapClick(overlay, latlng)
{
    if ( latlng === undefined ) return;
  
    var activeTracks = [];
    var ids = gTrackManager.getAllTrackIDs();
  
    for ( var k = 0; k < ids.length; k++ )
        if ( gTrackManager.getTrack( ids[k] ).isNearClick ( latlng, map.getZoom() ) )
            activeTracks.push( ids[k] );
            
    gActiveTrackManager.activateTracks(activeTracks);
}

///////////////////////////////////////////////////////////////////////////////
function GetTracks()
{
    var dateObject = new Date();
    var StartTime = dateObject.getTime();
    var requestTime;
    var curMapBounds = map.getBounds();

    map.clearOverlays();

    var boundsParams =
        "min_lat="  + curMapBounds.getSouthWest().lat() +
        "&min_lon=" + curMapBounds.getSouthWest().lng() +
        "&max_lat=" + curMapBounds.getNorthEast().lat() +
        "&max_lon=" + curMapBounds.getNorthEast().lng();
    
    var maxPoints   = document.getElementById("max_points").value;
    var maxSections = document.getElementById("max_sections").value;

    GDownloadUrl("cgi-bin/track_get.cgi?" + boundsParams + 
                 "&max_points=" + maxPoints +
                 "&max_sections=" + maxSections, function(data, status)
    {
        if (status != 200)
        {
            alert("Error: " + data);
            return;
        }
        
        var dateObject = new Date();
        requestTime = (dateObject.getTime() - StartTime)/1000;
        var json = eval('(' + data + ')');
        var tracks   = json.tracks;
        var sections = json.sections;
        var debug    = json.debug;

        var numPoints = 0;
        
        gTrackManager.clear();
        
        //delete table of active tracks
        $('#click_events').empty();
        
        var sw = map.getBounds().getSouthWest();
        var ne = map.getBounds().getNorthEast();
        var boundingLine = new GPolyline( [new GLatLng(sw.lat(), sw.lng()), 
                                              new GLatLng(sw.lat(), ne.lng()),
                                              new GLatLng(ne.lat(), ne.lng()),
                                              new GLatLng(ne.lat(), sw.lng()), 
                                              new GLatLng(sw.lat(), sw.lng())], 
                                              "#0000ff", 2, 1, {clickable: false} );
                                         
        map.addOverlay( boundingLine );
        
        //parsing tracks
        for ( var tr = 0; tr < tracks.length; tr++ )
        {
            var newTrack = new Track( tracks[tr], POLYLINE_STATUS_INVISIBLE );
            gTrackManager.addTrack( newTrack, tracks[tr].idtrack );
        }
            
        // parsing sections and adding corresponding polylines
        for ( var sect = 0; sect < sections.length; sect++ )
        {
            var curSection = gTrackManager.getTrack( sections[sect].idtrack ).addSection( sections[sect] );
            
            map.addOverlay( curSection.getPolyline() );
            
            numPoints += curSection.getPolyline().getVertexCount();
        }
        
        document.getElementById("statistic").style.display = "inherit";
        
        document.getElementById("statistic").innerHTML = 
            "Кол-во треков: " + tracks.length + " (на сервере " + debug.total_tracks + ")<br/>" + 
            "Кол-во секций: "+ sections.length + "; Кол-во точек: " + numPoints;

        //filling information for user about request
        // document.getElementById("num_tracks").innerHTML = "Кол-во треков: " + tracks.length + " (на сервере " + debug.total_tracks + "); Кол-во секций: "+ sections.length;
        // document.getElementById("num_points").innerHTML = "Кол-во точек: " + numPoints;
        // document.getElementById("request_time").innerHTML = "Request time: " + requestTime + " secs (server: " + debug.total_time + ")";
        // document.getElementById("request_size").innerHTML = "Request size: " + Math.round(data.length/1024) + " KBytes";
    });
}

///////////////////////////////////////////////////////////////////////////////
function UploadSubmit()
{
    if ( !document.getElementById("track_short_description").value )
    {
        alert("Add short description to track!");
        return false;
    }
    
    if ( uploadTrackType == "file" && !document.getElementById("track_file").value )
    {
        alert("Select file to upload!");
        return false;
    }
    
    if ( uploadTrackType == "url" && !document.getElementById("track_url").value )
    {
        alert("Enter url with file to upload!");
        return false;
    }
    
    document.getElementById("upload_track_tags").value = tagManagerAdd.getIDsWithState(1).join(' ');
    return true;
}

///////////////////////////////////////////////////////////////////////////////
function onKeydown(event)
{
    if (!event.target)
        event.target=event.srcElement;
        
    if ( event.keyCode == 85 && event.target.nodeName == "HTML" )
    {
        GetTracks();
    }
}

function setHelpVisible(visible)
{
    if (visible)
    {
        document.getElementById("full_help").style.display = "inherit";
        document.getElementById("short_help").style.display = "none";
    }
    else
    {
        document.getElementById("full_help").style.display = "none";
        document.getElementById("short_help").style.display = "inherit";
    }
}

var g_isVisibleVTPanel = true;
function toggleVisibleTracksPanel(event)
{
    if (g_isVisibleVTPanel)
    {
        $('#visible_tracks_td').css('display', 'none');
        $('#vt_separator').html('&gt;<br/>&gt;<br/>&gt;');
    }
    else
    {
        $('#visible_tracks_td').css('display', 'table-cell');
        $('#visible_tracks_td').width(300);
        $('#vt_separator').html('&lt;<br/>&lt;<br/>&lt;');
    }
    map.checkResize();
    g_isVisibleVTPanel = !g_isVisibleVTPanel;
}

function showAboutPage(event)
{
    // alert('Done');
    $('#about').dialog({width:800, 
                        modal: true, 
                        title: 'О проекте'});
    // event.preventDefault();
    // event.stopPropagation();

}